#include "system.h"
#include "gui.h"
#include "lcd.h"
#include "int.h"

#include "clock.h"

void init() {
	EXCLK_DDR|=1<<EXCLK_BIT;
	HC245_OE_DDR|=1<<HC245_OE_BIT;
	DDRD&=~(0x20);
	PORTD|=0X20;
	
	DISEN_EXCLK;
	DISEN_245;
	
	DATA_OUPUT();

	active_line = 0;

	//Init i2c bus
	i2c_init();

	//Init LCD Display and touch behaviour
    LCD_Init();
	Touch_Init();

	//Init GUI
	GUI_init();
	GUI_setHeader("Boot screen", HEADER_MODE_CLOCK);
	GUI_writeLine("Initialized user interface...", WHITE);
	GUI_writeLine("Initialized TFT display", WHITE);

	//Init clock interrupt timer for refreshing the time (main screen)
	GUI_writeLine("Initializing clock/timer...", WHITE);
	initTimer();
	initClock();

	//Init motors and slot parameters (create structs, fill with sample data)
	GUI_writeLine("Initializing motors...", WHITE);
	initMotors();
	initSlotValueStruct();
}

//Store the value contained in "iValue" as the "valueType" of slot "slotNumber"
void storeValue(uint8 slotNumber, enum PARAM valueType, uint8 iValue) {
    uint16 addr = STARTADDR + (SLOTINTERVAL*slotNumber);
    switch(valueType){
        case TIME_INTERVAL_H:
			allSlotValues[slotNumber].time_interval_h = iValue;
			eeprom_write_byte(addr, iValue);
            break;
        case TIME_INTERVAL_MIN:
			allSlotValues[slotNumber].time_interval_min = iValue;
			addr+=1;
			eeprom_write_byte(addr, iValue);
            break;
		case FILL_LEVEL:
			allSlotValues[slotNumber].fill_level = iValue;
			addr+=2;
			eeprom_write_byte(addr, iValue);
            break;
		case DOSE:
			allSlotValues[slotNumber].dose = iValue;
			addr+=3;
			eeprom_write_byte(addr, iValue);
            break;
        case SLOT_ACTIVE:
			allSlotValues[slotNumber].slot_active = iValue;
            addr+=4;
			eeprom_write_byte(addr, iValue);
            break;
		case LAST_INJ_Y:
			allSlotValues[slotNumber].last_inj_y = iValue;
            addr+=5;
			eeprom_write_byte(addr, iValue);
            break;
		case LAST_INJ_M:
			allSlotValues[slotNumber].last_inj_m = iValue;
            addr+=6;
			eeprom_write_byte(addr, iValue);
            break;
		case LAST_INJ_D:
			allSlotValues[slotNumber].last_inj_d = iValue;
            addr+=7;
			eeprom_write_byte(addr, iValue);
            break;
		case LAST_INJ_H:
			allSlotValues[slotNumber].last_inj_h = iValue;
            addr+=8;
			eeprom_write_byte(addr, iValue);
            break;
		case LAST_INJ_MIN:
			allSlotValues[slotNumber].last_inj_min = iValue;
            addr+=9;
			eeprom_write_byte(addr, iValue);
            break;
        default: break;
    }        
};

void storeValue16(uint8 slotNumber, enum PARAM valueType, int16 iValue) {
    uint16 addr = STARTADDR + (SLOTINTERVAL*slotNumber);		

	switch(valueType){
        case FINE_TUNING_Y:
			allSlotValues[slotNumber].fine_tuning_y = iValue;
            addr+=10;
			eeprom_write_byte(addr, make8(iValue,1));
			eeprom_write_byte(addr+1, make8(iValue,0));
			break;
		default: break;
	}
}

//Load the "valueType"-value of slot "slotNumber" from storage
//returns the result as an uint8
void loadValue(uint8 slotNumber, enum PARAM valueType, uint8* iValue) {
	uint16 addr = STARTADDR + (SLOTINTERVAL*slotNumber);


    switch(valueType){
        case TIME_INTERVAL_H:
			*iValue = eeprom_read_byte(addr);
            break;
        case TIME_INTERVAL_MIN:
			addr+=1;
			*iValue = eeprom_read_byte(addr);
            break;
		case FILL_LEVEL:
			addr+=2;
			*iValue = eeprom_read_byte(addr);
            break;
		case DOSE:
			addr+=3;
			*iValue = eeprom_read_byte(addr);
            break;
        case SLOT_ACTIVE:
            addr+=4;
			*iValue = eeprom_read_byte(addr);
            break;
		case LAST_INJ_Y:
            addr+=5;
			*iValue = eeprom_read_byte(addr);
            break;
		case LAST_INJ_M:
            addr+=6;
			*iValue = eeprom_read_byte(addr);
            break;
		case LAST_INJ_D:
            addr+=7;
			*iValue = eeprom_read_byte(addr);
            break;
		case LAST_INJ_H:
            addr+=8;
			*iValue = eeprom_read_byte(addr);
            break;
		case LAST_INJ_MIN:
            addr+=9;
			*iValue = eeprom_read_byte(addr);
            break;
		case FINE_TUNING_Y:
            addr+=10;
			*iValue = eeprom_read_byte(addr);
			*(iValue+1) = eeprom_read_byte(addr+1);
            break;
        default: break;
    }        
};

void loadAllValues(){
	uint8 i;

	for(i=0;i<7;i++){
		loadValue(i, TIME_INTERVAL_H, &(allSlotValues[i].time_interval_h));
		loadValue(i, TIME_INTERVAL_MIN, &(allSlotValues[i].time_interval_min));
		loadValue(i, FILL_LEVEL, &(allSlotValues[i].fill_level));
		loadValue(i, DOSE, &(allSlotValues[i].dose));
		loadValue(i, SLOT_ACTIVE, &(allSlotValues[i].slot_active));
		loadValue(i, LAST_INJ_Y, &(allSlotValues[i].last_inj_y));
		loadValue(i, LAST_INJ_M, &(allSlotValues[i].last_inj_m));
		loadValue(i, LAST_INJ_D, &(allSlotValues[i].last_inj_d));
		loadValue(i, LAST_INJ_H, &(allSlotValues[i].last_inj_h));
		loadValue(i, LAST_INJ_MIN, &(allSlotValues[i].last_inj_min));
		loadValue(i, FINE_TUNING_Y, &(allSlotValues[i].fine_tuning_y));
	}
}

void saveAllValues(){
	uint8 i;

	for(i=0;i<7;i++){
		storeValue(i, TIME_INTERVAL_H, allSlotValues[i].time_interval_h);
		storeValue(i, TIME_INTERVAL_MIN, allSlotValues[i].time_interval_min);
		storeValue(i, FILL_LEVEL, allSlotValues[i].fill_level);
		storeValue(i, DOSE, allSlotValues[i].dose);
		storeValue(i, SLOT_ACTIVE, allSlotValues[i].slot_active);
		storeValue(i, LAST_INJ_Y, allSlotValues[i].last_inj_y);
		storeValue(i, LAST_INJ_M, allSlotValues[i].last_inj_m);
		storeValue(i, LAST_INJ_D, allSlotValues[i].last_inj_d);
		storeValue(i, LAST_INJ_H, allSlotValues[i].last_inj_h);
		storeValue(i, LAST_INJ_MIN, allSlotValues[i].last_inj_min);
		storeValue16(i, FINE_TUNING_Y, allSlotValues[i].fine_tuning_y);
	}

}


void bootstrap(){
	uint8 i,j=7,
		  min, h, d, m, y;
	getTimeAndDate(&y, &m, &d, &h, &min);

	for(i=0;i<7;i++){
		allSlotValues[i].time_interval_h = 0;
		allSlotValues[i].time_interval_min = i+10;
		allSlotValues[i].fill_level = 60;
		allSlotValues[i].dose = 60- (i*10);	
		allSlotValues[i].slot_active = 1;
		allSlotValues[i].last_inj_y = y;
		allSlotValues[i].last_inj_m = m;
		allSlotValues[i].last_inj_d = d;
		allSlotValues[i].last_inj_h = h;
		allSlotValues[i].last_inj_min = min;
		j--;
	}
	allSlotValues[4].slot_active = 0;

	saveAllValues(allSlotValues);
}

void initSlotValueStruct(){
	bootstrap();
	loadAllValues();

	//initialize Errorlog
	lastErrorPointer = -1;
	for(uint8 i=0;i<MAX_LINES;i++)	ErrorLog[i].h = 255;
}

uint8 checkIfInjectionNeeded(){
    uint8 i,
		  min, h, d, m, y,
		  next_inj_h, next_inj_min,
		  dosed = 0;
	int16 timeLeft[7][2];

	getTimeLeft(timeLeft);
	getTimeAndDate(&y, &m, &d, &h, &min);
        
    for(i=0;i<7;i++){

		if(allSlotValues[i].slot_active){
	        if( timeLeft[i][0]<=0 && timeLeft[i][1] <=0)
			{
				dosed = 1;
				if(allSlotValues[i].dose <= allSlotValues[i].fill_level){

					if(allSlotValues[i].dose != 0){
						//dosing screen
						GUI_drawBox(0,HEADER_HEIGHT+1, OFFSET_WIDTH, OFFSET_HEIGHT - (LINE_HEIGHT + MARGIN_TOP + HEADER_HEIGHT + 1), RED);
						sprintf(string, "dosing %d ml at slot %d", allSlotValues[i].dose, i+1);
						LCD_write_english_string(80, 100, string, WHITE, RED); 

						inject(i, allSlotValues[i].dose + 60-allSlotValues[i].fill_level);
					}
					//save to RAM & EEPROM
					storeValue(i, LAST_INJ_MIN, min);
					storeValue(i, LAST_INJ_H, h);
					if(allSlotValues[i].last_inj_d != d)		storeValue(i, LAST_INJ_D, d);
					if(allSlotValues[i].last_inj_m != m)		storeValue(i, LAST_INJ_M, m);
					if(allSlotValues[i].last_inj_d != y)		storeValue(i, LAST_INJ_Y, y);
					storeValue(i, FILL_LEVEL, allSlotValues[i].fill_level -= allSlotValues[i].dose);
				}
				else{
					sprintf(string, "S%i failed: Dose %i but %i left", i+1, allSlotValues[i].dose, allSlotValues[i].fill_level);
					logError(ERROR, string);
					storeValue(i, SLOT_ACTIVE, 0);
				}
			}
		}
	}
	return dosed;
}

void getTimeLeft(int16* timeLeft){	//timeLeft is a two dimensional array!
	uint8 min, h_orig, d, m, y, i;
	int16	 h,
			 next_min;
	getTimeAndDate(&y, &m, &d, &h_orig, &min);

	for(i=0;i<7;i++){
		if(allSlotValues[i].slot_active){

			h = getPastHours(allSlotValues[i].last_inj_h,
						 	 allSlotValues[i].last_inj_d,
						 	 allSlotValues[i].last_inj_m,
						 	 allSlotValues[i].last_inj_y,
							 h_orig, d, m, y);

			next_min	= allSlotValues[i].last_inj_min + allSlotValues[i].time_interval_min;
			if(h > allSlotValues[i].time_interval_h || (h == allSlotValues[i].time_interval_h && min > next_min)){
				if(min < next_min){
										timeLeft[i*2+1] 	= -min -60+next_min;
										timeLeft[i*2]		= allSlotValues[i].time_interval_h - h +1;

				}else			  {		timeLeft[i*2+1]		= next_min - min;
										timeLeft[i*2]		= allSlotValues[i].time_interval_h - h;
				}
				
			}else{
				if(min > next_min){	
										timeLeft[i*2+1] 	= 60 - min + next_min;
										timeLeft[i*2]		= allSlotValues[i].time_interval_h - h - 1;
				}else			  {		timeLeft[i*2+1]		= next_min - min;
										timeLeft[i*2]		= allSlotValues[i].time_interval_h - h;
				}
			}

		}else{
			timeLeft[i*2] = -5000;
			timeLeft[i*2+1] = -5000;
		}
	}
}

void logError(enum LOG_LEVEL level, unsigned char* message){
	if(level){
		uint8 min, h, d, m, y;
		getTimeAndDate(&y, &m, &d, &h, &min);
		unsigned char array[4];
		lastErrorPointer++;
		if(lastErrorPointer >= MAX_LINES)	lastErrorPointer = 0;
		ErrorLog[lastErrorPointer].min = min;
		ErrorLog[lastErrorPointer].h = h;
		strcpy(ErrorLog[lastErrorPointer].message, message);
	}
	GUI_setErrorMsg(level, message);
}
